#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include "pcie_mem.h"

#define PCIE_PATH_MAX_LEN 256

int enable_pcie_device(const char *device_address)
{
    char path[PCIE_PATH_MAX_LEN];
    snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/enable", device_address);

    FILE *file = fopen(path, "w");
    if (file == NULL) {
        return -errno;
    }

    size_t written = fwrite("1", sizeof(char), 1, file);
    fclose(file);

    if (written != 1) {
        return -EIO;
    }

    return 0;
}

int disable_pcie_device(const char *device_address)
{
    char path[PCIE_PATH_MAX_LEN];
    snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/remove", device_address);

    FILE *file = fopen(path, "w");
    if (file == NULL) {
        return -errno;
    }

    size_t written = fwrite("1", sizeof(char), 1, file);
    fclose(file);

    if (written != 1) {
        return -EIO;
    }

    return 0;
}

int is_pcie_device_enabled(const char *device_address)
{
    char path[PCIE_PATH_MAX_LEN];
    snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/enable", device_address);
    
    FILE *file = fopen(path, "r");
    if (file == NULL) {
        return -1;
    }

    char buffer[16];
    if (fgets(buffer, sizeof(buffer), file) == NULL) {
        fclose(file);
        return -1;
    }
    fclose(file);

    /* 去除换行符 */
    buffer[strcspn(buffer, "\n")] = '\0';

    if (strcmp(buffer, "1") == 0) {
        return true;
    } else if (strcmp(buffer, "0") == 0) {
        return false;
    } else {
        return -1;
    }
}

int init_pcie_mem(struct pcie_mem_ctx *ctx, uint32_t phys_base, size_t map_size)
{
    int ret = 0;

    /* 打开 /dev/mem 用于内存映射 */
    ctx->fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (ctx->fd < 0) {
        printf("打开/dev/mem失败!");
        return -errno;
    }

    ctx->phys_base = phys_base;
    ctx->map_size = map_size;

    /* 将物理内存映射到我们的地址空间 */
    ctx->map_base = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
                         MAP_SHARED, ctx->fd, phys_base);
    if (ctx->map_base == MAP_FAILED) {
        printf("映射物理内存失败!");
        ret = -errno;
        goto err_close_fd;
    }

    printf("已将物理地址0x%08x映射到虚拟地址%p (大小: 0x%zx)\n",
            phys_base, ctx->map_base, map_size);

    return 0;

/* 错误处理标签：关闭文件描述符 */
err_close_fd:
    close(ctx->fd);
    ctx->fd = -1;
    return ret;
}

void cleanup_pcie_mem(struct pcie_mem_ctx *ctx)
{
    if (ctx->map_base) {
        munmap(ctx->map_base, ctx->map_size);
        ctx->map_base = NULL;
    }

    if (ctx->fd >= 0) {
        close(ctx->fd);
        ctx->fd = -1;
    }
}
